#include <streams.h>
#include <initguid.h>
#include "IMediaBridgeSourceFilter.h"
#include "MediaBridgeSourceFilter.h"

HRESULT CMediaBridgeSourceFilterPin::OnThreadCreate(void)
{
	return CSourceStream::OnThreadCreate();
}
HRESULT CMediaBridgeSourceFilterPin::OnThreadDestroy(void)
{
	return CSourceStream::OnThreadDestroy();
}

HRESULT CMediaBridgeSourceFilterPin::OnThreadStartPlay(void)
{
	return CSourceStream::OnThreadStartPlay();
}
	
HRESULT CMediaBridgeSourceFilterPin::Stop(void)
{
#if DEBUG
	System::Diagnostics::Debug::WriteLine("CMediaBridgeSourceFilterPin::Stop");
#endif
	return CSourceStream::Stop();
}
	
CMediaBridgeSourceFilterPin::CMediaBridgeSourceFilterPin(HRESULT *phr, CSource *pFilter) :  CSourceStream(NAME("RtspSource Stream"), 
																							 phr, 
																							 pFilter, 
																							 L"Out")
{
	m_rtSampleTime = 0;
}

CMediaBridgeSourceFilterPin::~CMediaBridgeSourceFilterPin()
{
#if DEBUG
	System::Diagnostics::Debug::WriteLine("~CMediaBridgeSourceFilterPin()");
#endif
}

// Override the version that offers exactly one media type
HRESULT CMediaBridgeSourceFilterPin::GetMediaType(CMediaType *pMediaType)
{
	/* Create video info struct to 
	 * populate media type information */
    VIDEOINFO *pvi = (VIDEOINFO *)pMediaType->AllocFormatBuffer(sizeof(VIDEOINFO));

	/* Would this ever really happen? */
    if (NULL == pvi) return S_OK;

	/* Zero out the memory */
	ZeroMemory(pvi, sizeof(VIDEOINFO));

	/* Plugin in our FOURCC */
	pvi->bmiHeader.biCompression = m_fourCC;   
	
	/* 24 bits per pixel */
    pvi->bmiHeader.biBitCount = m_bitCount;

	/* Header size */
    pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

	/* Video width */
    pvi->bmiHeader.biWidth = m_videoWidth;

	/* Video height */
    pvi->bmiHeader.biHeight = m_videoHeight;

	/* Video Planes */
    pvi->bmiHeader.biPlanes = 1;

	/* The max size a video frame will be */
    pvi->bmiHeader.biSizeImage = GetBitmapSize(&pvi->bmiHeader);
	
	/* ?? */
    pvi->bmiHeader.biClrImportant = 0;

	/* This stuff is pretty much ignored in this situation */
	pvi->AvgTimePerFrame = UNITS/30;
	
	/* we want the whole image area rendered */ 
    SetRectEmpty(&(pvi->rcSource)); 

	/* no particular destination rectangle */ 
    SetRectEmpty(&(pvi->rcTarget)); 
	
	/* Set the Major-type of the media for DShow */
    pMediaType->SetType(&MEDIATYPE_Video);

	/* Set the structure type so DShow knows 
	 * how to read the structure */
    pMediaType->SetFormatType(&FORMAT_VideoInfo);

	/* If our codec uses temporal compression */
    pMediaType->SetTemporalCompression(FALSE);
		
	GUID subTypeGUID;

	if(IsEqualGUID(m_mediaSubType, nullGuid))
	{
		subTypeGUID = GetBitmapSubtype(&pvi->bmiHeader);
	}
	else
	{
		subTypeGUID = m_mediaSubType;
	}

	//Set the subtype
    pMediaType->SetSubtype(&subTypeGUID);

	/* Set the max sample size */
    pMediaType->SetSampleSize(pvi->bmiHeader.biSizeImage);
	
	/* Get our video header to configure */
	VIDEOINFOHEADER *vihOut = (VIDEOINFOHEADER *)pMediaType->Format();
	
	/* 30 Fps - Not important */
	vihOut->AvgTimePerFrame = UNITS / 30;

	return S_OK;
}

HRESULT CMediaBridgeSourceFilterPin::DecideBufferSize(IMemAllocator* pMemAlloc, 
										 ALLOCATOR_PROPERTIES* pProperties)
{
	/* Thread-saftey */
    CAutoLock cAutoLockShared(&m_cSharedState);

	HRESULT hr = S_OK;
	{
		VIDEOINFO *pvi = (VIDEOINFO *)m_mt.Format();
		pProperties->cBuffers = 1;
		pProperties->cbBuffer = pvi->bmiHeader.biSizeImage;		
	}
	
	/* Ask the allocator to reserve us some sample memory, NOTE the function
	 * can succeed (that is return NOERROR) but still not have allocated the
	 * memory that we requested, so we must check we got whatever we wanted */
	ALLOCATOR_PROPERTIES Actual;

	hr = pMemAlloc->SetProperties(pProperties, &Actual);

	if (FAILED(hr)) 
	{
		return hr;
	}

	/* Is this allocator unsuitable? */ 
	if (Actual.cbBuffer < pProperties->cbBuffer) 
	{
		return E_FAIL;
	}

	return S_OK;
}

HRESULT CMediaBridgeSourceFilterPin::FillBuffer(IMediaSample *pSample)
{
	/* Thread-saftey */
    CAutoLock cAutoLockShared(&m_cSharedState);

	/* Get the source filter ref */
	CMediaBridgeSourceFilter *source = (CMediaBridgeSourceFilter*)this->m_pFilter;

	/* Get the callback instance */
	IMediaBridgeCallback *callback = source->GetCallBackInstance();
		
	BYTE *pData;

	if(!callback)
	{
		return S_OK;
	}		

	HRESULT hr = S_OK;

	/* Call our managed code */
	hr = callback->FillBuffer(pSample);

	if(hr != 0)
	{
		/* We might want to do something
		 * here in the future */
	}		

	return S_OK;
}

void CMediaBridgeSourceFilterPin::SetVideoDimensions(int VideoPixelWidth, 
													 int VideoPixelHeight, 
													 int BitCount, 
													 DWORD FourCC, 
													 GUID MediaSubType)
{
	m_videoWidth = VideoPixelWidth;
	m_videoHeight = VideoPixelHeight;
	m_bitCount = BitCount;
	m_fourCC = FourCC;
	m_mediaSubType = MediaSubType;
}

STDMETHODIMP CMediaBridgeSourceFilterPin::Notify(IBaseFilter *pSelf, Quality q)
{
    return E_NOTIMPL;
}

/* For IUnknown */
STDMETHODIMP CMediaBridgeSourceFilterPin::NonDelegatingQueryInterface(REFIID riid, void ** ppv)
{
	CheckPointer(ppv,E_POINTER);

	return CSourceStream::NonDelegatingQueryInterface(riid, ppv);
}